import math
import pygame
from pygame.locals import *
import os
import copy
import re
import time
import G
import sys
import tkFileDialog, Tkinter

images = dict()
imageweights = dict()

types = {'energy': 0,
         'physical': 1,
         'collide': 2,
         'explosion': 3}

class Delayed_Action(object):
   __slots__ = ('function',
                'delay',
                'max_delay',
                'max_times',
                'do_at_start',
                'last_updated')
   
   def __init__(self, function, delay, max_times=-1, do_at_start=True):
      self.function = function
      self.max_delay = delay
      self.max_times = max_times
      self.do_at_start = do_at_start
   
   def start(self):
      G.action_queue.append(self)
      self.last_updated = time.time()
      if self.do_at_start:
         self.delay = self.max_delay
      else:
         self.delay = 0.0
   
   def update(self):
      now = time.time()
      frame_time = now - self.last_updated
      self.last_updated = now
      self.delay += frame_time
      if self.delay >= self.max_delay:
         self.function()
         self.delay -= self.max_delay
         if self.max_times != -1:
            self.max_times -= 1
            if self.max_times == 0:
               return True
      return False

def after(f, delay):
   Delayed_Action(f, delay, 1, False).start()

def askopenfilename(**args):
   f = tkFileDialog.askopenfilename(**args)
   Tkinter._default_root.destroy()
   return f

def asksaveasfilename(**args):
   f = tkFileDialog.asksaveasfilename(**args)
   Tkinter._default_root.destroy()
   return f

def constrain(num, min, max):
   if num < min:
      return min
   elif num > max:
      return max
   else:
      return num

def deepnest(ls, level):
   """Recursively deepens ls so that each of its elements is a list of depth
      level-1, each element of those lists is a list of depth level-2, and
      so on."""
   if level <= 1:
      if not is_sequence(ls):
         return [ls]
      else:
         return ls
   else:
      if not is_sequence(ls):
         return [deepnest(ls, level-1)]
      else:
         def f(l):
            return deepnest(l, level-1)
         return map(f, ls)

def double_split(s, div1, div2, f=None):
   parts = s.split(div1)
   result = list()
   for p in parts:
      more = p.split(div2)
      if f is not None:
         for i,el in enumerate(more):
            try:
               more[i] = f(el)
            except ValueError:
               pass
      result.append(more)
   return result

def error(message):
   print "Error: %s" % message
   sys.exit(1)

def is_sequence(s):
   return isinstance(s, list) or isinstance(s, tuple)

def load_image(name, colorkey=None, orientation=0):
   if name in images.keys():
      image, rect = images[name][orientation]
      return [image.copy(), copy.copy(rect)]
   else:
      images[name] = [[] for i in range(0,4)]
   fullname = os.path.join('sprites', name)
   try:
      image = pygame.image.load(fullname)
   except pygame.error, message:
      print "Cannot load image:", name
      raise SystemExit, message
   image = image.convert()
   if colorkey != -1:
      if colorkey is None:
         colorkey = image.get_at((0,0))
      image.set_colorkey(colorkey, RLEACCEL)
   
   images[name][0] = (image, image.get_rect())
   
   for i, angle in enumerate((270, 180, 90)):
      rotated = pygame.transform.rotate(image, angle)
      images[name][i+1] = (rotated, rotated.get_rect())
   
   imageweights[name] = proportion_filled(image, name)
   im, rect = images[name][orientation]
   return (im.copy(), copy.deepcopy(rect))

def maxdepth(ls):
   if not is_sequence(ls):
      return 0
   else:
      return max([maxdepth(el) for el in ls]) + 1

def minabs(a1, a2):
   if math.fabs(a1) < math.fabs(a2):
      return a1
   else:
      return a2

def proportion_filled(image, name):
   if name in imageweights.keys():
      return imageweights[name]
   w = image.get_width()
   h = image.get_height()
   key = image.get_colorkey()
   if key is not None:
      filled = 0
      total = w * h
      for i in range(0, w):
         for j in range(0,h):
            at = image.get_at((i,j))
            if at[0] != key[0] or at[1] != key[1] or at[2] != key[2]:
               filled += 1
      result = filled * 1.0 / total
   else:
      result = 1.0
   imageweights[name] = result
   return result

def rectradius(rect):
   return math.sqrt(1.0 * (rect.height ** 2 + rect.width ** 2)) / 2

def shallownest(ls, level):
   """Makes ls a list of depth at least level by repeatedly making it the
      first element of a new list until it has the desired depth."""
   while maxdepth(ls) < level:
      ls = [ls]
   return ls

def sign(f1):
   if f1 > 0.0:
      return 1
   elif f1 < 0.0:
      return -1
   else:
      return 0

def str_convert(s):
   """Utility method for parsing strings. Attempts to parse the string and 
      return it as a boolean, sequence/nested sequence, int, and float in that
      order. If none of these works, returns the original string."""
   try:
      return eval(s)
   except:
      return s
